package com.tzx.dropzoomscrollviewdemo;

import android.content.Context;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.widget.ScrollView;

public class PullZoomScrollView extends ScrollView{

    private final int NORMAL = 0;
    private final int PULL_UP = 1;
    private final int PULL_DOWN = 2;
    private final float FACTION = 2.5f;

    private int mCurrentMode = NORMAL;

    private View mHeaderContainer;
    private View mContentView;

    private Rect mRect;

    private float mTouchSlop;
    private float mInitY;
    private float mLastY;
    private int mHeaderHeight = 0;
    private int maxShowHeaderHeight;

    private Runnable mSmoothToTop;
    private Runnable mSmoothToEnd;

    private OnScrollUpListener onScrollUpListener;

    public PullZoomScrollView(Context context) {
        super(context);
        init(context);
    }

    public PullZoomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public PullZoomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        setOverScrollMode(OVER_SCROLL_NEVER);
        //mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mTouchSlop = 10;
        mHeaderHeight = -1;
        mRect = new Rect();
        maxShowHeaderHeight = gettDisplayHeight(context) / 3;
    }

    public void setHeaderContainer(View view) {
        mHeaderContainer = view;
    }

    @Override
    protected void onFinishInflate() {
        mContentView = getChildAt(0);
        if (mContentView != null && mHeaderContainer == null) {
            mHeaderContainer = ((ViewGroup) mContentView).getChildAt(0);
        }
        View blankView = new View(getContext());
        ((ViewGroup)mContentView).addView(blankView);
        ViewGroup.LayoutParams layoutParams = blankView.getLayoutParams();
        layoutParams.height = 500;
        super.onFinishInflate();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        if (mHeaderHeight == -1 && mHeaderContainer != null) {
            mHeaderHeight = mHeaderContainer.getHeight();
        }

        if (mContentView != null) {
            mRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() == 0) {
            mLastY = mInitY = ev.getY();
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int delta = 0;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                mLastY = mInitY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:

                float y = event.getY();

                delta = (int) (y - mLastY);

                if (mCurrentMode == NORMAL) {

                    if (Math.abs(delta) >= mTouchSlop) {

                        if (delta >= 1.0f && canPullDown()) {        //pulldown
                            mInitY = mLastY;
                            mCurrentMode = PULL_DOWN;
                        } else if (delta <= -1.0f && canPullUp()) {
                            mInitY = mLastY;
                            mCurrentMode = PULL_UP;
                        }
                    }
                }
                mLastY = y;
                //pullDown
                if (mCurrentMode == PULL_DOWN) {
                    if (pullDown(y - mInitY))
                        return true;

                } else if (mCurrentMode == PULL_UP) {   //pullUp
                    if (pullUp(y - mInitY)) return true;
                }
                break;
            case MotionEvent.ACTION_UP:

                if (mCurrentMode == PULL_DOWN) {
                    scrollBackToTop();
                } else if (mCurrentMode == PULL_UP) {
                    scrollBackToEnd();
                }
                mCurrentMode = NORMAL;
                break;
            default:
                break;
        }

        return super.onTouchEvent(event);
    }

    private boolean canPullDown() {
        if (getOverScrollMode() == OVER_SCROLL_ALWAYS) {
            setOverScrollMode(OVER_SCROLL_NEVER);
        }
        return getScrollY() == 0 ||(mHeaderContainer != null && mHeaderContainer.getHeight() > mHeaderHeight);
    }


    private boolean canPullUp() {
        if (getOverScrollMode() == OVER_SCROLL_NEVER) {
            setOverScrollMode(OVER_SCROLL_ALWAYS);
        }
        if (mContentView != null) {
            return mContentView.getHeight()<= getHeight() + getScrollY();
        }
        return false;
    }

    private boolean pullDown(float dY) {
        if (dY <= 0.0f) {
            mCurrentMode = NORMAL;
            return false;
        }

        if (mSmoothToTop != null) {
            ((SmoothToTopRunnable) mSmoothToTop).stopAnimation();
        }

        LayoutParams layoutParams = (LayoutParams) mHeaderContainer.getLayoutParams();

        int newValue = (int) (dY / (FACTION * layoutParams.height / mHeaderHeight));
        layoutParams.height = (int) (mHeaderHeight + newValue);
        mHeaderContainer.setLayoutParams(layoutParams);

        return true;
    }

    private boolean pullUp(float dY) {
        if (dY >= 0.0f) {
            mCurrentMode = NORMAL;
            return false;
        }

        if (mSmoothToEnd != null) ((SmoothToTopRunnable) mSmoothToEnd).stopAnimation();
        int newValue = (int) (dY / FACTION);
        mContentView.layout(mRect.left, mRect.top + newValue, mRect.right, mRect.bottom + newValue);
        return true;
    }


    private void scrollBackToTop() {
        if (mSmoothToTop == null) {
            mSmoothToTop = new SmoothToTopRunnable(mHeaderContainer);
        }
        ((SmoothToTopRunnable) mSmoothToTop).stopAnimation();
        ((SmoothToTopRunnable) mSmoothToTop).startAnimation(mHeaderContainer.getHeight(), mHeaderHeight);
    }

    private void scrollBackToEnd() {
        if (mSmoothToEnd == null) {
            mSmoothToEnd = new SmoothToEndRunnable(mContentView);
        }
        ((SmoothToTopRunnable) mSmoothToEnd).stopAnimation();
        ((SmoothToTopRunnable) mSmoothToEnd).startAnimation(mContentView.getTop(), mRect.top);
    }

    class SmoothToTopRunnable implements Runnable {

        public int curY;
        public int targetY;
        public long startTime;
        public boolean isFinished;
        public static final long DURATION = 250L;
        public View view;

        public SmoothToTopRunnable(View view) {
            this.view = view;

        }

        @Override
        public void run() {

            if (!isFinished) {
                long time = SystemClock.currentThreadTimeMillis();
                float factor = interpolator.getInterpolation((float) (time - startTime) / DURATION);

                ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                if (factor <= 0.0f) {
                    if (layoutParams.height != targetY) {

                        layoutParams.height = targetY;
                        view.setLayoutParams(layoutParams);
                    }
                    isFinished = true;
                } else {
                    layoutParams.height = (int) (targetY + (curY - targetY) * factor);
                    view.setLayoutParams(layoutParams);
                    post(this);
                }
            }
        }

        public void startAnimation(int curY, int targetY) {
            this.curY = curY;
            this.targetY = targetY;
            startTime = SystemClock.currentThreadTimeMillis();
            isFinished = false;
            post(this);
        }

        public void stopAnimation() {

            removeCallbacks(this);
            isFinished = true;

        }
    }

    private Interpolator interpolator = new Interpolator() {

        @Override
        public float getInterpolation(float input) {

            return (float) Math.pow((1.0f - input), 9);
        }
    };

    class SmoothToEndRunnable extends SmoothToTopRunnable {

        public SmoothToEndRunnable(View view) {
            super(view);
        }

        @Override
        public void run() {

            if (!isFinished) {
                long time = SystemClock.currentThreadTimeMillis();
                float factor = interpolator.getInterpolation((float) (time - startTime) / DURATION);

                int value = (int) ((curY - targetY) * factor);

                if (factor <= 0.0f) {
                    if (value != 0) {
                        view.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
                    }
                    isFinished = true;
                } else {
                    view.layout(mRect.left, mRect.top + value, mRect.right, mRect.bottom + value);
                    post(this);
                }
            }
        }

    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float v = mHeaderContainer.getBottom() - getScrollY();
        float f = mHeaderHeight - v;
//        if ((f > 0.0F) && (f < mHeaderHeight)) {
//            int i = (int) (0.65D * f);
//            mHeaderContainer.scrollTo(0, -i);
//        } else if (mHeaderContainer.getScrollY() != 0) {
//            mHeaderContainer.scrollTo(0, 0);
//        }
        if (onScrollUpListener != null) {
            if (v <= maxShowHeaderHeight) {
                float progress = (new Float(v) / new Float(maxShowHeaderHeight));
                onScrollUpListener.onScrollUp(progress);
            } else {
                onScrollUpListener.OnScrollDown();
            }
        }
    }

    public interface OnScrollUpListener {
        public void onScrollUp(float progress);

        public void OnScrollDown();
    }

    public void setMaxShowHeaderHeight(int height) {
        maxShowHeaderHeight = height;
    }


    public void setOnScrollUpListener(OnScrollUpListener onScrollUpListener) {
        this.onScrollUpListener = onScrollUpListener;
    }

    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static int gettDisplayHeight(Context ctx){
        DisplayMetrics metric = new DisplayMetrics();
        WindowManager winManager = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
        winManager.getDefaultDisplay().getMetrics(metric);
        int height = metric.heightPixels; // 屏幕宽度（像素）
        return height;
    }
}
